test(printplay): #204 extract recto-verso back reorder to pure method + 9 tests - #500
Merged
Merged
Conversation
… + 9 tests The Print & Play PDF reverses each grid ROW of card backs before rendering so backs line up with fronts when the sheet is flipped along its horizontal edge. This alignment contract was inlined in PrintAndPlayDocument.Compose() with zero unit coverage — it could only be exercised by rendering a QuestPDF document and visually inspecting the back alignment. A regression (a full-array mirror instead of per-row, or flipping rows too) produces printed sheets whose backs mismatch their fronts — unusable sheets caught only at print time. Extracted output-neutral into the pure generic static PrintAndPlayDocument.ReorderBacksForRectoVerso<T>(backs, nbColumns) (identical computation: ToJaggedArray -> reverse each row -> Flatten) and pinned the contract with 9 additive tests: - full rows (per-row reversal, row order preserved) - trailing short row (singleton reverses to itself, stays at tail) - single column (horizontal flip is identity) - one short row / width-2 / empty / single-back degenerate cases - byte[] grounding (production element type) - cell-wise alignment semantic: output[row][col] == input[row][cols-1-col] Full suite on clean master base: 291 passed / 0 failed / 5 skipped (no regression). Dispatch #204 gamma (cont. po-2024) — pivot sanctioned by dispatch qbw8vq ("sinon pivote PdfManager layout math"). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
|
[NanoClaw] test(printplay): extract recto-verso back reorder to pure generic method + 8 tests. Extraction: The inline Tests: 8 Fact tests covering: full evenly-divisible rows, trailing short row, single column, short row within larger column count, even width-2, empty backs, single back, production byte[] element type, and a cell-wise alignment contract (output[row][col] == input[row][cols-1-col]). Well-documented doc comments explain the per-row reversal semantics vs full mirror. No secrets, no issues. LGTM. |
This was referenced Jun 16, 2026
jsboige
added a commit
that referenced
this pull request
Jun 17, 2026
…tests (#512) The Print & Play PDF (PrintAndPlayDocument) computed four derived layout quantities inline in Compose() with ZERO unit coverage: nbColumns, nbRows, nbCardsPerPage, nbPages. A regression in any of them (rounding instead of truncating columns, forgetting the header reserve, flooring instead of ceiling the page count) silently changes how many sheets print and how cards distribute across them, caught only by rendering and eyeballing the PDF. Extracts the arithmetic output-neutral into a pure, deterministic ComputePageGeometry (no QuestPDF dependency, no I/O) + a PrintPlayPageGeometry readonly struct. The call site preserves the exact computation (verified by reading the old inline block before extraction), so rendered output is byte-for-byte unchanged. 13 contract tests pin the layout contract additively (mirrors #500's recto-verso reorder test conventions): - columns: configured honored when >0; zero/negative fall back to floor division; floor-TRUNCATION not rounding (the fragile bit) - rows: floor division of content height - cardsPerPage = rows x columns - pages: ceil(count/perPage); partial last sheet still prints; exact multiple; zero cards -> zero pages - header reserves pageHeight/10 (matches ComposePage header band) - margin subtracted from both dimensions Gate-safe: additive tests + output-neutral extraction. The latent divide-by-zero when cardsPerPage==0 (card larger than content area) is preserved as-is, NOT guarded, and flagged in the XML doc for a separate behavior-change PR. Suite: 342 passed / 0 failed / 5 skipped (baseline 329 + 13 new). Contributes to #204. Refs #500 (sibling recto-verso extraction). Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
jsboige
added a commit
that referenced
this pull request
Jun 17, 2026
… method + 9 tests (#521) PdfManager.GenerateAlternateFaceAndBack (the #119 contract) assembles a single PDF where each card's BACK is emitted immediately before its FRONT so that on a recto-verso sheet each back lines up behind its matching front, and the ORIGINAL CardSet order is preserved (so back-less Rules keep their place and appear first). This ordering is a fragile contract with ZERO unit coverage — a regression emitting front-then-back, dropping the back-less cards, or reordering by back-presence silently misaligns every printed sheet, caught only by printing. Extracts the path-sequencing output-neutral into a pure, deterministic OrderImagesForAlternateFaceAndBack (static, no MagickImage, no I/O). The call site emits the exact same path sequence (verified by reading the old inline builder before extraction), so rendered output is byte-for-byte unchanged. 9 contract tests pin the contract additively (mirror #500 recto-verso reorder + #512 page-grid geometry conventions): - per-card: back-then-front when a back exists; front-only when no back; empty back == no back (the IsNullOrEmpty guard) - ordering: original CardSet order preserved (mixed backs NOT grouped); a back-less head card stays first (the Rules-first #119 guarantee) - counts: all-with-back doubles the slot count; all-without-back singles - pairing: back immediately precedes its OWN front (indices 2i, 2i+1) - degenerate: empty input emits nothing Gate-safe: additive tests + output-neutral extraction. Suite 351/0/5 (baseline 342 + 9 new). Contributes to #204. Refs #500/#512 (sibling recto-verso extractions), #119. Co-authored-by: Your <your.email@example.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
The Print & Play PDF (
PrintAndPlayDocument) prints card backs on the reverse of each sheet so a sheet can be flipped to read the back. To make each back line up behind its matching front when the sheet is turned along its horizontal edge, the backs of each grid row are reversed before rendering — a per-row reversal (not a full-array mirror), because a horizontal flip swaps left/right within each row while preserving row order.This fragile alignment contract was inlined in
Compose()with zero unit coverage:It could only be exercised by rendering a full QuestPDF document and visually inspecting the back alignment. A regression here (e.g. reversing the whole array instead of per-row, or flipping rows too) produces printed sheets whose backs mismatch their fronts — unusable sheets, only caught at print time.
Change
Output-neutral extraction + additive test coverage. No behavior change.
Extracted the inline composition into a pure, deterministic, generic static method:
The call site (
Compose()) now readsReorderBacksForRectoVerso(pageBackImages, nbColumns)— identical computation, just unit-testable.Added
PrintAndPlayRectoVersoContractTests(9 tests) pinning the contract:[B2,B1,B0,B5,B4,B3](per-row reversal, row order preserved; explicitly rejects a full-array mirror[B5..B0]).output[row][col] == input[row][cols-1-col]for every cell (the precise within-row mirror).The building blocks (
ToJaggedArray,Flatten) are already pinned byUtilityExtensionsLayoutTests; this PR pins the Print&Play-specific composition — the gap that test file does not cover.Verification
9/9pass.RowsetNb/rscountchanges, no workflow/rules touched.Scope
Dispatch
#204γ — test-coverage expansion on fragile pipeline contracts. Pivot sanctioned by dispatchqbw8vq("sinon pivote PdfManager layout math"). Additive only; no production behavior change.🤖 Worker po-2024